package com.gy.controller.common;

import com.alipay.api.internal.util.file.IOUtils;
import com.aliyun.oss.*;
import com.aliyun.oss.model.*;
import com.gy.File.FileUtils;
import com.gy.File.fileInfo;
import com.gy.config.AliOSSConfig;



import com.gy.service.FileInfoService;
import com.gy.utils.AliOSSUtils;
import com.gy.utils.Result;

import com.itextpdf.text.DocumentException;


import org.apache.pdfbox.Loader;
import org.apache.pdfbox.io.MemoryUsageSetting;
import org.apache.pdfbox.rendering.ImageType;
import org.apache.pdfbox.rendering.PDFRenderer;

import com.itextpdf.text.Document;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.imageio.ImageIO;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.*;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Image;
import com.itextpdf.text.pdf.PdfWriter;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
@Slf4j
@RestController
@Api(tags = "文件上传")
@CrossOrigin
public class UploadController {

    @Autowired
    private AliOSSUtils aliOSSUtils;

    @Autowired
    private AliOSSConfig aliOSSConfig;

    @Autowired
    private FileInfoService fileInfoService;

    @Autowired
    private ThreadPoolTaskExecutor taskExecutor;

    @Autowired
    private OSS ossClient;


    /**
     * 上传 PDF 或图片，统一转为图片页
     * @param file 上传的文件
     * @return 文件路径、页数等信息
     */
    @PostMapping("/upload/file")
    @ApiOperation("上传 PDF 或图片，统一转为图片页")
    public Result uploadPdfOrImage(MultipartFile file) {
        String originalFileName = file.getOriginalFilename();

        if (originalFileName == null || !FileUtils.isSupportedFileType(originalFileName)) {
            return Result.error("仅支持 PDF 或图片格式！");
        }

        try {
            byte[] fileBytes = file.getBytes();
            InputStream inputStream = new ByteArrayInputStream(fileBytes);

            String fileHash = calculateFileHash(inputStream);
            inputStream = new ByteArrayInputStream(fileBytes);

            // 检查文件是否已存在
            fileInfo existingFile = fileInfoService.getByFileHash(fileHash);
            if (existingFile != null) {
                String existingBasePath = existingFile.getFilePath(); // 数据库中存储的相对路径（如pdf-images/xxx/）
                String bucketName = aliOSSConfig.getBucketName();
                String endpoint = aliOSSConfig.getEndpoint();

                // 生成正确格式的URL：https://<Bucket>.<Endpoint>/<相对路径>page-1.jpg
                String firstImageUrl = "https://" + bucketName + "." + endpoint + "/" + existingBasePath + "page-1.jpg";

                Map<String, Object> data = new HashMap<>();
                data.put("imageBasePath", existingBasePath);
                data.put("pageCount", existingFile.getPageCount());
                data.put("firstImageUrl", firstImageUrl);
                data.put("fileHash", fileHash);
                data.put("isExist", true);

                return Result.success(data);
            }

            String bucketName = aliOSSConfig.getBucketName();
            String endpoint = aliOSSConfig.getEndpoint();
            // 生成相对路径（仅存储在数据库，不包含Bucket和Endpoint）
            String baseImagePath = "pdf-images/" + UUID.randomUUID() + "/";
            int pageCount = 0;

            if (FileUtils.isPdfFile(originalFileName)) {
                PDDocument document = Loader.loadPDF(IOUtils.toByteArray(inputStream));
                PDFRenderer renderer = new PDFRenderer(document);
                pageCount = document.getNumberOfPages();

                for (int i = 0; i < pageCount; i++) {
                    BufferedImage image = renderer.renderImageWithDPI(i, 145);
                    uploadImageToOss(bucketName, baseImagePath + "page-" + (i + 1) + ".jpg", image);
                }
                document.close();
            } else if (FileUtils.isImageFile(originalFileName)) {
                BufferedImage image = ImageIO.read(inputStream);
                uploadImageToOss(bucketName, baseImagePath + "page-1.jpg", image);
                pageCount = 1;
            }

            // 存数据库
            fileInfo fileInfo = new fileInfo();
            fileInfo.setFileName(originalFileName);
            fileInfo.setFilePath(baseImagePath); // 格式：pdf-images/uuid/
            fileInfo.setPageCount(pageCount);
            fileInfo.setFileHash(fileHash);
            fileInfoService.save(fileInfo);

            // 生成正确格式的URL：https://<Bucket>.<Endpoint>/<相对路径>page-1.jpg
            String firstImageUrl = "https://" + bucketName + "." + endpoint + "/" + baseImagePath + "page-1.jpg";

            Map<String, Object> data = new HashMap<>();
            data.put("imageBasePath", baseImagePath); // 相对路径，用于后续预览接口
            data.put("pageCount", pageCount);
            data.put("firstImageUrl", firstImageUrl); // 正确格式的完整URL
            data.put("isExist", false);

            return Result.success(data);

        } catch (Exception e) {
            log.error("上传处理失败", e);
            return Result.error("处理失败：" + e.getMessage());
        }
    }
    // 上传OSS的方法（需传入BucketName和相对路径key）
    private void uploadImageToOss(String bucketName, String key, BufferedImage image) throws IOException {
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        ImageIO.write(image, "jpg", os);
        byte[] imageBytes = os.toByteArray();

        InputStream input = new ByteArrayInputStream(imageBytes);
        ObjectMetadata metadata = new ObjectMetadata();
        metadata.setContentLength(imageBytes.length);
        metadata.setContentType("image/jpeg");

        // OSS上传需指定Bucket和相对路径key
        ossClient.putObject(bucketName, key, input, metadata);
    }

    // 计算文件的MD5哈希值（作为fileHash）
    private String calculateFileHash(InputStream inputStream) throws Exception {
        MessageDigest md = MessageDigest.getInstance("MD5");
        byte[] buffer = new byte[8192];
        int length;
        while ((length = inputStream.read(buffer)) != -1) {
            md.update(buffer, 0, length);
        }
        byte[] hashBytes = md.digest();

        // 转为16进制字符串
        StringBuilder sb = new StringBuilder();
        for (byte b : hashBytes) {
            sb.append(String.format("%02x", b));
        }
        return sb.toString();
    }

    /**
     * 预览文件
     *
     * @param response HTTP响应对象
     */
    @GetMapping(value = "/upload/preview")
    @ApiOperation("文件预览")
    public void showPresentation(HttpServletRequest request, HttpServletResponse response,
                                 @RequestParam("filePath") String filePath) throws IOException {
        String bucketName = aliOSSConfig.getBucketName();
        String objectKey = filePath.substring(filePath.lastIndexOf("/") + 1);

        if (!ossClient.doesObjectExist(bucketName, objectKey)) {
            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
            response.getWriter().write("预览文件不存在：" + filePath);
            return;
        }

        ObjectMetadata metadata = ossClient.getObjectMetadata(bucketName, objectKey);
        long fileSize = metadata.getContentLength();
        String contentType = metadata.getContentType() != null ?
                metadata.getContentType() :
                URLConnection.guessContentTypeFromName(objectKey);
        contentType = contentType != null ? contentType : "application/octet-stream";
        response.setContentType(contentType);
        response.setHeader("Accept-Ranges", "bytes");

        // 处理 Range 请求
        String range = request.getHeader("Range");
        long start = 0, end = fileSize - 1;
        boolean isPartial = false;

        if (range != null && range.startsWith("bytes=")) {
            isPartial = true;
            String[] parts = range.substring(6).split("-");
            try {
                start = Long.parseLong(parts[0]);
                if (parts.length > 1 && !parts[1].isEmpty()) {
                    end = Long.parseLong(parts[1]);
                }
            } catch (NumberFormatException e) {
                response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
                return;
            }
        }

        long contentLength = end - start + 1;
        response.setHeader("Content-Disposition", "inline; filename=" +
                URLEncoder.encode(objectKey, StandardCharsets.UTF_8.name()));

        if (isPartial) {
            response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
            response.setHeader("Content-Range", "bytes " + start + "-" + end + "/" + fileSize);
        }

        response.setHeader("Content-Length", String.valueOf(contentLength));

        // 请求 OSS 指定范围的数据流
        GetObjectRequest getObjectRequest = new GetObjectRequest(bucketName, objectKey);
        getObjectRequest.setRange(start, end);
        OSSObject ossObject = ossClient.getObject(getObjectRequest);

        try (InputStream inputStream = ossObject.getObjectContent();
             ReadableByteChannel inputChannel = Channels.newChannel(inputStream);
             WritableByteChannel outputChannel = Channels.newChannel(response.getOutputStream())) {

            ByteBuffer buffer = ByteBuffer.allocateDirect(64 * 1024);
            while (inputChannel.read(buffer) != -1) {
                buffer.flip();
                while (buffer.hasRemaining()) {
                    outputChannel.write(buffer);
                }
                buffer.clear();
                response.flushBuffer(); // 提前响应，减少阻塞
            }
        } catch (IOException e) {
            log.error("预览文件异常", e);
            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            response.getWriter().write("预览文件异常：" + e.getMessage());
        }
    }


    @GetMapping("/upload/pdfImages")
    @ApiOperation("获取PDF转换后的图片URL列表")
    public Result getPdfImages(@RequestParam("filePath") String filePath) {
        try {
            // 关键：对前端编码后的filePath进行解码（处理特殊字符）
            String decodedFilePath = URLDecoder.decode(filePath, StandardCharsets.UTF_8.name());

            // 处理完整URL的情况，提取相对路径部分
            if (decodedFilePath.startsWith("http")) {
                // 使用正则表达式匹配pdf-images/后面的UUID部分
                Pattern pattern = Pattern.compile("pdf-images/[a-f0-9]{8}-([a-f0-9]{4}-){3}[a-f0-9]{12}/");
                Matcher matcher = pattern.matcher(decodedFilePath);
                if (matcher.find()) {
                    decodedFilePath = matcher.group(0);
                } else {
                    return Result.error("URL格式不正确，无法提取PDF路径");
                }
            }

            // 确保路径以斜杠结尾
            if (!decodedFilePath.endsWith("/")) {
                decodedFilePath += "/";
            }

            // 查询数据库（使用解码后的原始路径）
            fileInfo fileInfo = fileInfoService.getByFilePath(decodedFilePath);
            if (fileInfo == null) {
                return Result.error("文件不存在");
            }

            int pageCount = fileInfo.getPageCount();
            List<String> imageUrls = new ArrayList<>();

            // 生成所有页面的图片URL（使用解码后的路径）
            for (int i = 1; i <= pageCount; i++) {
                String imageUrl = "https://" +  // 明确添加HTTPS协议
                        aliOSSConfig.getBucketName() + "." +
                        aliOSSConfig.getEndpoint() + "/" +
                        decodedFilePath + "page-" + i + ".jpg";
                imageUrls.add(imageUrl);
            }

            Map<String, Object> data = new HashMap<>();
            data.put("imageUrls", imageUrls);
            data.put("pageCount", pageCount);

            return Result.success(data);
        } catch (Exception e) {
            log.error("获取图片列表失败", e);
            return Result.error("获取图片列表失败：" + e.getMessage());
        }
    }

    @PostMapping("/upload/appendPdf")
    @ApiOperation("追加上传PDF或图片，增加图片页")
    public Result appendPdf(@RequestParam("fileToAppend") MultipartFile fileToAppend,
                            @RequestParam("filePath") String filePath) {
        String bucketName = aliOSSConfig.getBucketName();
        String baseImagePath = null;

        try {
            // 1. 解析基础路径
            if (filePath.startsWith("http")) {
                // 处理完整URL的情况，提取相对路径部分
                URL url = new URL(filePath);
                String path = url.getPath().substring(1); // 去掉开头的"/"

                // 提取基础路径 (pdf-images/uuid/)
                Pattern pattern = Pattern.compile("(pdf-images/[a-f0-9-]+/)");
                Matcher matcher = pattern.matcher(path);
                if (matcher.find()) {
                    baseImagePath = matcher.group(1);
                } else {
                    return Result.error("URL格式不正确，无法提取基础路径");
                }
            } else {
                // 如果不是完整URL，假设传入的是相对路径
                baseImagePath = filePath;
            }

            // 2. 确保路径以斜杠结尾
            if (!baseImagePath.endsWith("/")) {
                baseImagePath += "/";
            }

            // 3. 获取文件信息
            fileInfo fileInfo = fileInfoService.getByFilePath(baseImagePath);
            if (fileInfo == null) {
                return Result.error("文件记录不存在");
            }

            int currentPageCount = fileInfo.getPageCount();
            int newPages = 0;
            String originalFileName = fileToAppend.getOriginalFilename();

            if (originalFileName == null) {
                return Result.error("文件名不能为空");
            }

            // 4. 处理上传的文件
            byte[] fileBytes = fileToAppend.getBytes();
            InputStream inputStream = new ByteArrayInputStream(fileBytes);

            if (FileUtils.isPdfFile(originalFileName)) {
                // 处理PDF文件
                PDDocument document = Loader.loadPDF(IOUtils.toByteArray(inputStream));
                PDFRenderer renderer = new PDFRenderer(document);
                int appendPageCount = document.getNumberOfPages();
                newPages = appendPageCount;

                for (int i = 0; i < appendPageCount; i++) {
                    BufferedImage image = renderer.renderImageWithDPI(i, 144);
                    // 新页面的页码从当前总页数+1开始
                    String imageKey = baseImagePath + "page-" + (currentPageCount + i + 1) + ".jpg";
                    uploadImageToOss(bucketName, imageKey, image);
                }
                document.close();
            } else if (FileUtils.isImageFile(originalFileName)) {
                // 处理图片文件
                BufferedImage image = ImageIO.read(inputStream);
                if (image == null) {
                    return Result.error("图片格式不支持");
                }
                // 新页面只有一页
                newPages = 1;
                String imageKey = baseImagePath + "page-" + (currentPageCount + 1) + ".jpg";
                uploadImageToOss(bucketName, imageKey, image);
            } else {
                return Result.error("仅支持PDF或图片格式");
            }

            // 5. 更新数据库
            fileInfo.setPageCount(currentPageCount + newPages);
            fileInfoService.saveOrUpdate(fileInfo);

            // 6. 返回成功信息
            Map<String, Object> data = new HashMap<>();
            data.put("newPageCount", newPages);
            data.put("totalPageCount", currentPageCount + newPages);
            return Result.success(data);

        } catch (Exception e) {
            log.error("追加上传失败", e);
            return Result.error("追加上传失败：" + e.getMessage());
        }
    }



    @PostMapping("/deletePdfPage")
    @ApiOperation("删除PDF中的特定页面（图片页）")
    public Result deletePdfPage(@RequestParam("filePath") String filePath,
                                @RequestParam("pageNumber") int pageNumber) {
        log.info("开始删除PDF页面，文件路径: {}, 页码: {}", filePath, pageNumber);

        String bucketName = aliOSSConfig.getBucketName();
        String baseImagePath = null;

        try {
            // 1. 解析基础路径
            if (filePath.startsWith("http")) {
                // 处理完整URL的情况，提取相对路径部分
                URL url = new URL(filePath);
                String path = url.getPath().substring(1); // 去掉开头的"/"

                // 提取基础路径 (pdf-images/uuid/)
                Pattern pattern = Pattern.compile("(pdf-images/[a-f0-9-]+/)");
                Matcher matcher = pattern.matcher(path);
                if (matcher.find()) {
                    baseImagePath = matcher.group(1);
                } else {
                    return Result.error("URL格式不正确，无法提取基础路径");
                }
            } else {
                // 如果不是完整URL，假设传入的是相对路径
                baseImagePath = filePath;
            }

            // 2. 确保路径以斜杠结尾
            if (!baseImagePath.endsWith("/")) {
                baseImagePath += "/";
            }

            // 3. 获取文件信息
            fileInfo fileInfo = fileInfoService.getByFilePath(baseImagePath);
            if (fileInfo == null) {
                return Result.error("文件记录不存在");
            }

            int totalPages = fileInfo.getPageCount();

            // 4. 检查页码是否有效
            if (pageNumber < 1 || pageNumber > totalPages) {
                return Result.error("无效的页码：" + pageNumber + "，总页数为：" + totalPages);
            }

            // 5. 删除指定页码的图片
            String pageKey = baseImagePath + "page-" + pageNumber + ".jpg";
            if (ossClient.doesObjectExist(bucketName, pageKey)) {
                ossClient.deleteObject(bucketName, pageKey);
                log.info("已删除图片：{}", pageKey);
            } else {
                log.warn("图片不存在：{}", pageKey);
            }

            // 6. 重命名后续所有图片（页码前移）
            for (int i = pageNumber + 1; i <= totalPages; i++) {
                String oldKey = baseImagePath + "page-" + i + ".jpg";
                String newKey = baseImagePath + "page-" + (i - 1) + ".jpg";

                if (ossClient.doesObjectExist(bucketName, oldKey)) {
                    // 复制文件到新位置
                    ossClient.copyObject(
                            bucketName,
                            oldKey,
                            bucketName,
                            newKey
                    );

                    // 删除原文件
                    ossClient.deleteObject(bucketName, oldKey);
                    log.info("已重命名图片：{} -> {}", oldKey, newKey);
                }
            }

            // 7. 更新数据库
            fileInfo.setPageCount(totalPages - 1);
            fileInfoService.saveOrUpdate(fileInfo);

            log.info("成功删除第 {} 页，剩余页数: {}", pageNumber, fileInfo.getPageCount());
            return Result.success("成功删除页面");

        } catch (Exception e) {
            log.error("删除PDF页面时发生错误", e);
            return Result.error("删除页面失败: " + e.getMessage());
        }
    }
    @PostMapping("/deleteFile")
    @ApiOperation("删除OSS中的文件")
    public Result deleteFile(@RequestParam("fileHash") String fileHash) {
        log.info("开始删除OSS中的文件，文件哈希: {}", fileHash);

        // 1️⃣ **查询数据库，获取文件路径**
        fileInfo fileInfo = fileInfoService.getByFileHash(fileHash);
        if (fileInfo == null) {
            return Result.error("文件不存在，无法删除");
        }

        String filePath = fileInfo.getFilePath();
        String bucketName = aliOSSConfig.getBucketName();
        String objectKey = filePath.substring(filePath.lastIndexOf("/") + 1);

        try {
            // 2️⃣ **检查文件是否存在**
            if (!ossClient.doesObjectExist(bucketName, objectKey)) {
                return Result.error("OSS中不存在该文件: " + filePath);
            }

            // 3️⃣ **删除 OSS 中的文件**
            ossClient.deleteObject(bucketName, objectKey);
            log.info("文件 {} 已成功删除", objectKey);

            // 4️⃣ **从数据库删除记录**
            fileInfoService.deleteByFileHash(fileHash);
            log.info("数据库记录已删除，文件哈希: {}", fileHash);

            return Result.success("文件删除成功");
        } catch (OSSException | ClientException e) {
            log.error("删除文件时发生错误", e);
            return Result.error("删除文件失败: " + e.getMessage());
        }
    }

    @PostMapping("/updatePdfPageOrder")
    @ApiOperation("更新PDF页面顺序（图片页重命名）")
    public Result updatePdfPageOrder(@RequestParam("filePath") String filePath,
                                     @RequestBody List<Integer> pageOrder) {
        log.info("更新PDF页面顺序，文件路径: {}, 新顺序: {}", filePath, pageOrder);

        String bucketName = aliOSSConfig.getBucketName();
        String baseImagePath = null;

        try {
            // 1. 解析基础路径
            if (filePath.startsWith("http")) {
                // 处理完整URL的情况，提取相对路径部分
                URL url = new URL(filePath);
                String path = url.getPath().substring(1); // 去掉开头的"/"

                // 提取基础路径 (pdf-images/uuid/)
                Pattern pattern = Pattern.compile("(pdf-images/[a-f0-9-]+/)");
                Matcher matcher = pattern.matcher(path);
                if (matcher.find()) {
                    baseImagePath = matcher.group(1);
                } else {
                    return Result.error("URL格式不正确，无法提取基础路径");
                }
            } else {
                // 如果不是完整URL，假设传入的是相对路径
                baseImagePath = filePath;
            }

            // 2. 确保路径以斜杠结尾
            if (!baseImagePath.endsWith("/")) {
                baseImagePath += "/";
            }

            // 3. 获取文件信息
            fileInfo fileInfo = fileInfoService.getByFilePath(baseImagePath);
            if (fileInfo == null) {
                return Result.error("文件记录不存在");
            }

            int totalPages = fileInfo.getPageCount();

            // 4. 验证新的页面顺序是否有效
            if (pageOrder.size() != totalPages) {
                return Result.error("页面顺序的数量与总页数不一致");
            }

            // 检查是否包含所有页码（1到totalPages）
            Set<Integer> uniquePages = new HashSet<>(pageOrder);
            if (uniquePages.size() != totalPages) {
                return Result.error("页面顺序包含重复值");
            }

            for (int i = 1; i <= totalPages; i++) {
                if (!uniquePages.contains(i)) {
                    return Result.error("页面顺序缺少页码: " + i);
                }
            }

            // 5. 创建临时目录存储中间文件
            String tempDir = baseImagePath + "temp-" + UUID.randomUUID() + "/";

            // 6. 第一步：将文件复制到临时位置（使用新顺序）
            for (int newIndex = 0; newIndex < pageOrder.size(); newIndex++) {
                int originalIndex = pageOrder.get(newIndex);
                String sourceKey = baseImagePath + "page-" + originalIndex + ".jpg";
                String tempKey = tempDir + "page-" + (newIndex + 1) + ".jpg";

                // 复制到临时位置
                ossClient.copyObject(
                        bucketName,
                        sourceKey,
                        bucketName,
                        tempKey
                );
            }

            // 7. 第二步：删除原始图片
            for (int i = 1; i <= totalPages; i++) {
                String sourceKey = baseImagePath + "page-" + i + ".jpg";
                if (ossClient.doesObjectExist(bucketName, sourceKey)) {
                    ossClient.deleteObject(bucketName, sourceKey);
                }
            }

            // 8. 第三步：将文件从临时位置移回原始位置
            for (int i = 1; i <= totalPages; i++) {
                String tempKey = tempDir + "page-" + i + ".jpg";
                String targetKey = baseImagePath + "page-" + i + ".jpg";
                // 复制回原始位置
                ossClient.copyObject(
                        bucketName,
                        tempKey,
                        bucketName,
                        targetKey
                );

                // 删除临时文件
                ossClient.deleteObject(bucketName, tempKey);
            }

            // 9. 清理临时目录
            ossClient.deleteObject(bucketName, tempDir);

            log.info("成功更新PDF页面顺序");
            return Result.success("页面顺序更新成功");

        } catch (Exception e) {
            log.error("更新PDF页面顺序时发生错误", e);
            return Result.error("更新页面顺序失败: " + e.getMessage());
        }
    }





    /**
     * 将图片合并为PDF并下载
     */
    @GetMapping("/upload/mergeToPdf")
    @ApiOperation("将图片合并为PDF并下载")
    public void mergeImagesToPdf(HttpServletResponse response,
                                 @RequestParam("filePath") String filePath) {
        String bucketName = aliOSSConfig.getBucketName();
        String endpoint = aliOSSConfig.getEndpoint();
        String baseImagePath = null;

        try {
            // 1. 解析文件路径（与现有逻辑保持一致）
            String decodedFilePath = URLDecoder.decode(filePath, StandardCharsets.UTF_8.name());
            if (decodedFilePath.startsWith("http")) {
                // 从URL提取基础路径（如pdf-images/uuid/）
                Pattern pattern = Pattern.compile("pdf-images/[a-f0-9]{8}-([a-f0-9]{4}-){3}[a-f0-9]{12}/");
                Matcher matcher = pattern.matcher(decodedFilePath);
                if (!matcher.find()) {
                    response.sendError(HttpServletResponse.SC_BAD_REQUEST, "无效的文件路径格式");
                    return;
                }
                baseImagePath = matcher.group(0);
            } else {
                baseImagePath = decodedFilePath;
            }

            // 确保路径以斜杠结尾
            if (!baseImagePath.endsWith("/")) {
                baseImagePath += "/";
            }

            // 2. 查询文件信息获取总页数
            fileInfo fileInfo = fileInfoService.getByFilePath(baseImagePath);
            if (fileInfo == null) {
                response.sendError(HttpServletResponse.SC_NOT_FOUND, "文件记录不存在");
                return;
            }
            int totalPages = fileInfo.getPageCount();

            // 3. 初始化PDF文档
            Document document = new Document();
            response.setContentType("application/pdf");
            // 处理文件名（去除原扩展名）
            String originalName = fileInfo.getFileName();
            String pdfName = originalName.contains(".")
                    ? originalName.substring(0, originalName.lastIndexOf(".")) + ".pdf"
                    : originalName + ".pdf";
            String encodedFileName = URLEncoder.encode(pdfName, StandardCharsets.UTF_8.name());
            response.setHeader("Content-Disposition", "attachment; filename=\"" + encodedFileName + "\"");

            // 4. 流式生成PDF
            PdfWriter.getInstance(document, response.getOutputStream());
            document.open();

            // 5. 逐页添加图片
            for (int i = 1; i <= totalPages; i++) {
                String imageKey = baseImagePath + "page-" + i + ".jpg";
                // 检查图片是否存在
                if (!ossClient.doesObjectExist(bucketName, imageKey)) {
                    log.warn("图片不存在：{}", imageKey);
                    continue;
                }

                // 从OSS获取图片流
                try (OSSObject ossObject = ossClient.getObject(bucketName, imageKey);
                     InputStream imageStream = ossObject.getObjectContent()) {

                    // 读取图片并设置到PDF
                    byte[] imageBytes = IOUtils.toByteArray(imageStream);
                    log.info("图片{}读取成功，字节长度：{}", imageKey, imageBytes.length); // 关键日志
                    Image image = Image.getInstance(imageBytes);
                    // 自适应页面大小
                    document.setPageSize(new com.itextpdf.text.Rectangle(image.getWidth(), image.getHeight()));
                    document.newPage();
                    document.add(image);
                }
            }

            // 6. 完成PDF生成
            document.close();
            response.flushBuffer();

        } catch (DocumentException e) {
            log.error("PDF文档生成失败", e);
            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
        } catch (IOException e) {
            log.error("IO操作异常", e);
            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
        } finally {
            // 确保资源释放
            try {
                response.getOutputStream().close();
            } catch (IOException e) {
                log.error("关闭输出流失败", e);
            }
        }
    }

    /**
     * 修改阿里云OSS上的PDF文件的某一页（替换为新图片或PDF页）
     * @param filePath 目标PDF的路径（支持URL或相对路径）
     * @param pageNumber 要替换的页码（从1开始）
     * @param newFile 新的图片或PDF文件（用于替换指定页）
     * @return 替换结果
     */
    @PostMapping("/updatePdfPage")
    @ApiOperation("修改阿里云OSS上的PDF文件的某一页")
    public Result updatePdfPage(
            @RequestParam("filePath") String filePath,
            @RequestParam("pageNumber") int pageNumber,
            @RequestParam("newFile") MultipartFile newFile) {
        log.info("开始修改PDF页面，文件路径: {}, 页码: {}", filePath, pageNumber);

        String bucketName = aliOSSConfig.getBucketName();
        String baseImagePath = null;

        try {
            // 1. 参数校验
            if (newFile.isEmpty()) {
                return Result.error("新文件不能为空");
            }
            String originalFileName = newFile.getOriginalFilename();
            if (originalFileName == null || !FileUtils.isSupportedFileType(originalFileName)) {
                return Result.error("仅支持PDF或图片格式的文件");
            }
            if (pageNumber < 1) {
                return Result.error("页码必须大于0");
            }

            // 2. 解析基础路径（和其他接口保持一致的逻辑）
            if (filePath.startsWith("http")) {
                // 从URL中提取相对路径（如 pdf-images/uuid/）
                URL url = new URL(filePath);
                String path = url.getPath().substring(1); // 去掉开头的"/"
                Pattern pattern = Pattern.compile("(pdf-images/[a-f0-9-]+/)");
                Matcher matcher = pattern.matcher(path);
                if (matcher.find()) {
                    baseImagePath = matcher.group(1);
                } else {
                    return Result.error("URL格式不正确，无法提取PDF路径");
                }
            } else {
                // 直接使用相对路径
                baseImagePath = filePath;
            }
            // 确保路径以斜杠结尾
            if (!baseImagePath.endsWith("/")) {
                baseImagePath += "/";
            }

            // 3. 验证目标文件是否存在
            fileInfo fileInfo = fileInfoService.getByFilePath(baseImagePath);
            if (fileInfo == null) {
                return Result.error("目标PDF文件不存在");
            }
            int totalPages = fileInfo.getPageCount();
            if (pageNumber > totalPages) {
                return Result.error("页码超出范围（总页数：" + totalPages + "）");
            }

            // 4. 处理新文件（转换为图片，只取第一页）
            BufferedImage newImage;
            byte[] fileBytes = newFile.getBytes();
            InputStream inputStream = new ByteArrayInputStream(fileBytes);

            if (FileUtils.isPdfFile(originalFileName)) {
                // 若为PDF，提取第一页转换为图片
                PDDocument document = Loader.loadPDF(IOUtils.toByteArray(inputStream));
                if (document.getNumberOfPages() < 1) {
                    document.close();
                    return Result.error("PDF文件无有效页面");
                }
                PDFRenderer renderer = new PDFRenderer(document);
                newImage = renderer.renderImageWithDPI(0, 144); // 只取第一页
                document.close();
            } else if (FileUtils.isImageFile(originalFileName)) {
                // 若为图片，直接读取
                newImage = ImageIO.read(inputStream);
                if (newImage == null) {
                    return Result.error("图片格式不支持");
                }
            } else {
                return Result.error("不支持的文件类型");
            }

            // 5. 替换OSS中的旧图片
            String targetKey = baseImagePath + "page-" + pageNumber + ".jpg";
            // 上传新图片覆盖旧图片
            uploadImageToOss(bucketName, targetKey, newImage);
            log.info("已替换OSS中的图片：{}", targetKey);

            fileInfoService.saveOrUpdate(fileInfo);

            return Result.success("第" + pageNumber + "页已成功替换");

        } catch (Exception e) {
            log.error("替换PDF页面失败", e);
            return Result.error("替换页面失败：" + e.getMessage());
        }
    }

    /**
     * 批量更新PDF页面旋转（支持对多个页面设置不同旋转角度）
     * @param requestData 请求体，包含文件路径和批量处理的页面信息
     * @return 批量处理结果
     */
    @PostMapping("/batchUpdatePdfPages")
    @ApiOperation("批量更新PDF页面旋转")
    public Result batchUpdatePdfPages(@RequestBody Map<String, Object> requestData) {
        log.info("开始批量更新PDF页面旋转，请求参数: {}", requestData);

        // 1. 解析请求参数
        String filePath = (String) requestData.get("filePath");
        List<Map<String, Integer>> pages = (List<Map<String, Integer>>) requestData.get("pages");

        String bucketName = aliOSSConfig.getBucketName();
        String baseImagePath = null;

        try {
            // 2. 参数校验
            if (filePath == null || filePath.isEmpty()) {
                return Result.error("文件路径不能为空");
            }
            if (pages == null || pages.isEmpty()) {
                return Result.error("页面列表不能为空");
            }
            // 校验每个页面的参数合法性
            for (Map<String, Integer> page : pages) {
                Integer pageNumber = page.get("pageNumber");
                Integer rotation = page.get("rotation");
                if (pageNumber == null || pageNumber < 1) {
                    return Result.error("页码必须大于0");
                }
                if (rotation == null || !Arrays.asList(90, 180, 270).contains(rotation)) {
                    return Result.error("旋转角度仅支持90、180、270度");
                }
            }

            // 3. 解析基础路径（与其他接口保持一致）
            if (filePath.startsWith("http")) {
                URL url = new URL(filePath);
                String path = url.getPath().substring(1); // 去掉开头的"/"
                Pattern pattern = Pattern.compile("(pdf-images/[a-f0-9-]+/)");
                Matcher matcher = pattern.matcher(path);
                if (matcher.find()) {
                    baseImagePath = matcher.group(1);
                } else {
                    return Result.error("URL格式不正确，无法提取PDF路径");
                }
            } else {
                baseImagePath = filePath;
            }
            if (!baseImagePath.endsWith("/")) {
                baseImagePath += "/";
            }

            // 4. 验证目标文件存在性
            fileInfo fileInfo = fileInfoService.getByFilePath(baseImagePath);
            if (fileInfo == null) {
                return Result.error("目标PDF文件不存在");
            }
            int totalPages = fileInfo.getPageCount();

            // 5. 校验所有页码是否在有效范围
            for (Map<String, Integer> page : pages) {
                int pageNumber = page.get("pageNumber");
                if (pageNumber > totalPages) {
                    return Result.error("页码 " + pageNumber + " 超出范围（总页数：" + totalPages + "）");
                }
            }

            // 6. 批量处理每个页面
            int successCount = 0;
            List<String> failPages = new ArrayList<>();

            for (Map<String, Integer> page : pages) {
                int pageNumber = page.get("pageNumber");
                int rotation = page.get("rotation");
                String targetKey = baseImagePath + "page-" + pageNumber + ".jpg";
                log.info("处理第{}页，目标OSS路径：{}", pageNumber, targetKey); // 新增日志

                try {
                    // 6.1 下载当前页面图片
                    if (!ossClient.doesObjectExist(bucketName, targetKey)) {
                        failPages.add("第" + pageNumber + "页（图片不存在）");
                        continue;
                    }
                    OSSObject ossObject = ossClient.getObject(bucketName, targetKey);
                    BufferedImage originalImage = ImageIO.read(ossObject.getObjectContent());
                    ossObject.close();

                    // 6.2 执行旋转处理
                    AffineTransform transform = new AffineTransform();
                    int width = originalImage.getWidth();
                    int height = originalImage.getHeight();
                    double radians = Math.toRadians(rotation);

                    // 根据角度调整旋转中心和尺寸
                    if (rotation == 90 || rotation == 270) {
                        // 90/270度旋转：宽高互换
                        transform.translate(height / 2.0, width / 2.0);
                        transform.rotate(radians);
                        transform.translate(-width / 2.0, -height / 2.0);
                    } else {
                        // 180度旋转：中心不变
                        transform.translate(width / 2.0, height / 2.0);
                        transform.rotate(radians);
                        transform.translate(-width / 2.0, -height / 2.0);
                    }

                    // 创建旋转后的图片
                    BufferedImage rotatedImage = new BufferedImage(
                            (rotation == 90 || rotation == 270) ? height : width,
                            (rotation == 90 || rotation == 270) ? width : height,
                            originalImage.getType()
                    );
                    Graphics2D g2d = rotatedImage.createGraphics();
                    g2d.setTransform(transform);
                    g2d.drawImage(originalImage, 0, 0, null);
                    g2d.dispose();

                    // 上传旋转后的图片覆盖原图片
                    uploadImageToOss(bucketName, targetKey, rotatedImage);
                    successCount++;
                    log.info("已批量旋转页面：{}", targetKey);

                } catch (Exception e) {
                    log.error("批量旋转第{}页失败", pageNumber, e);
                    failPages.add("第" + pageNumber + "页（处理异常）");
                }
            }


            fileInfoService.saveOrUpdate(fileInfo);

            // 8. 组装结果
            Map<String, Object> result = new HashMap<>();
            result.put("total", pages.size());
            result.put("success", successCount);
            result.put("fail", failPages);

            if (failPages.isEmpty()) {
                return Result.success("所有页面均旋转成功");
            } else {
                return Result.success("部分页面旋转成功");
            }

        } catch (Exception e) {
            log.error("批量更新PDF页面旋转失败", e);
            return Result.error("批量处理失败：" + e.getMessage());
        }
    }

    /**
     * 获取OSS中文件的大小
     *
     * @param filePath 文件路径
     * @return 文件大小（字节）
     */
    @GetMapping("/getFileSize")
    @ApiOperation("获取文件大小")
    public Result getFileSize(@RequestParam("filePath") String filePath) {
        log.info("开始获取文件大小，原始 filePath: {}", filePath);

        try {
            // 解码 filePath，确保路径安全
            String decodedPath = URLDecoder.decode(filePath, StandardCharsets.UTF_8.name());

            // 如果是完整 OSS URL，则提取相对路径部分
            if (decodedPath.startsWith("http")) {
                URL url = new URL(decodedPath);
                decodedPath = url.getPath(); // 得到 /pdf-images/uuid/page-1.jpg
                decodedPath = decodedPath.substring(1); // 去掉开头的 "/"
            }

            // 去掉文件名部分，只保留文件夹路径
            int lastSlashIndex = decodedPath.lastIndexOf('/');
            if (lastSlashIndex > 0) {
                decodedPath = decodedPath.substring(0, lastSlashIndex + 1); // 保留最后的 "/"
            }

            // 拼接合并 PDF 的 key：pdf-images/uuid/merged.pdf
            String mergedPdfKey = decodedPath ;

            String bucketName = aliOSSConfig.getBucketName();
            ObjectMetadata objectMetadata = ossClient.getObjectMetadata(bucketName, mergedPdfKey);
            long fileSize = objectMetadata.getContentLength();

            log.info("文件 {} 的大小为: {} 字节", mergedPdfKey, fileSize);
            return Result.success(fileSize);

        } catch (OSSException | ClientException e) {
            log.error("获取文件大小时发生 OSS 错误", e);
            return Result.error("获取文件大小失败: " + e.getMessage());
        } catch (Exception e) {
            log.error("解析路径失败", e);
            return Result.error("解析路径失败: " + e.getMessage());
        }
    }


    @PostMapping("/checkFileExists")
    @ApiOperation("检查文件是否已存在（秒传）")
    public Result checkFileExists(@RequestParam("fileHash") String fileHash,
                                  @RequestParam("fileName") String fileName) {
        log.info("检查文件是否存在，文件哈希: {}, 文件名: {}", fileHash, fileName);

        try {
            // 1. 查询数据库，检查是否有相同哈希值的文件
            fileInfo fileInfo = fileInfoService.getByFileHash(fileHash);

            if (fileInfo != null) {
                // 文件已存在，可以秒传
                log.info("文件已存在，可以秒传: {}", fileInfo.getFilePath());

                // 2. 创建一个新的文件记录，但使用已有的文件路径
                // 这里可以根据业务需求决定是否创建新记录或直接返回已有记录
                String newFilePath = fileInfo.getFilePath();

                return Result.success(new HashMap<String, Object>() {{
                    put("url", newFilePath);
                    put("isExist", true);
                }});
            } else {
                // 文件不存在，需要正常上传
                return Result.success(new HashMap<String, Object>() {{
                    put("isExist", false);
                }});
            }
        } catch (Exception e) {
            log.error("检查文件是否存在时发生错误", e);
            return Result.error("检查文件失败: " + e.getMessage());
        }
    }







}
